package csci6450.chickenFriedFS.metaServer.threads;

// <editor-fold defaultstate="collapsed" desc=" UML Marker "> 
import csci6450.chickenFriedFS.metaServer.MetaServer;
import csci6450.chickenFriedFS.metaServer.MetaServerLog;
import csci6450.chickenFriedFS.metaServer.NuggetServerInfoLog;
import csci6450.chickenFriedFS.metaServer.RuntimeSettings;
import csci6450.chickenFriedFS.nuggetCore.ChickenFile;
import csci6450.chickenFriedFS.nuggetCore.NuggetConstants;
import csci6450.chickenFriedFS.nuggetCore.NuggetServerInfo;
import csci6450.chickenFriedFS.nuggetCore.ThreadStatus;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

// #[regen=yes,id=DCE.F0D5401E-0985-F7A5-9F15-991DA4A3D8F8]
// </editor-fold>
/**
 * Class used to Listen for Client requests and send the response back depending
 * on what functiont the client is wanting to perform.
 * @author jacobschoen
 */
public class MetaServerClientListener extends Thread {

    /**
     * Socket for this server to run on.
     */
    private ServerSocket clientRequestServer;

    // <editor-fold defaultstate="collapsed" desc=" UML Marker "> 
    // #[regen=yes,id=DCE.3D8ECDEE-C845-9D58-12E2-7621FC57B17D]
    // </editor-fold> 
    public MetaServerClientListener() throws IOException {
        this.clientRequestServer = new ServerSocket(RuntimeSettings.getInstance().getClientListenerPort());
        System.out.println("MetaServer listening for Clients on port " + RuntimeSettings.getInstance().getClientListenerPort() + ".");

    }

    @Override
    public void run() {
        while (true) {
            try {
                Socket client = this.clientRequestServer.accept();
                Connect c = new Connect(client);
            } catch (Exception e) {
                Logger.getLogger(MetaServerClientListener.class.getName()).log(Level.SEVERE, null, e);
            }
        }
    }

    class Connect extends Thread {

        private Socket client = null;
        private ObjectInputStream inputStream = null;
        private ObjectOutputStream outputStream = null;

        public Connect() {
        }

        public Connect(Socket clientSocket) {
            this.client = clientSocket;
            try {
                this.inputStream = new ObjectInputStream(this.client.getInputStream());

                this.outputStream = new ObjectOutputStream(this.client.getOutputStream());
            } catch (Exception e1) {
                try {
                    this.client.close();
                } catch (Exception e) {
                    Logger.getLogger(Connect.class.getName()).log(Level.SEVERE, null, e);
                }
                return;
            }
            this.start();
        }

        @Override
        public void run() {
            try {
                System.out.println("Connection from user.");
                String function = (String) this.inputStream.readObject();
                System.out.println("Have function :="+function);
                if (function.equals(NuggetConstants.TIMESTAMP)) {
                    this.outputStream.writeObject(new Date());
                    this.outputStream.flush();
                } else if (function.equals(NuggetConstants.STOP)) {
                    MetaServer.getInstance().stop();
                } else if (function.startsWith(NuggetConstants.PUT)) {
                    //means the user wants to put something in the file system
                    int nuggetsNeeded = Integer.parseInt(function.substring(NuggetConstants.PUT.length() + 1));
                    if (nuggetsNeeded > 0) {
                        List<NuggetServerInfo> response = this.getLocations(nuggetsNeeded);
                        if (response.size() > 0) {
                            System.out.println("Sending the serverinfo :="+ response.size());
                            this.outputStream.writeObject(response);
                        } else {
                            this.outputStream.writeObject("There is not enough avalible space.");
                        }
                    }
                } else if (function.equals(NuggetConstants.LS)) {
                    this.outputStream.writeObject(MetaServerLog.getInstance().getCompleteChickenFiles());
                    this.outputStream.flush();
                } else if (function.startsWith(NuggetConstants.GET)) {
                    String filename = function.substring(NuggetConstants.GET.length() + 1);
                    this.outputStream.writeObject(MetaServerLog.getInstance().getChickenFile(filename));
                    this.outputStream.flush();
                } else if (function.startsWith(NuggetConstants.FAILURE)) {
                    String filename = function.substring(NuggetConstants.FAILURE.length() + 1);
                    MetaServerLog.getInstance().removeChickenFile(filename);
                    this.outputStream.writeObject(NuggetConstants.SUCCESS);
                    this.outputStream.flush();
                } else if (function.startsWith(NuggetConstants.RM)) {
                    String filename = function.substring(NuggetConstants.RM.length() + 1);
                    ChickenFile file = MetaServerLog.getInstance().getChickenFile(filename);
                    if (file == null) {
                        this.outputStream.writeObject("File Not Found.");
                        this.outputStream.flush();
                    } else {
                        //now get rid of the damn thing
                        //need to loop through the the nuggets and tell the Nugget Servers to remove each one.
                        ArrayList<ThreadStatus> statusList = new ArrayList<ThreadStatus>();
                        for (int i = 0; i < file.getNuggets().size(); i = i + 1) {
                            ThreadStatus status = new ThreadStatus(file.getNuggets().get(i).getNuggetIdentifier());
                            RemoveNuggetFromServer get = new RemoveNuggetFromServer(file.getNuggets().get(i), status);
                            statusList.add(status);
                            get.start();
                        }
                        //wait for them to finish
                        //this is not working
                        boolean done = false;
                        boolean success = true;
                        while (!done) {
                            done = true;
                            success = true;
                            for (int i = 0; i < statusList.size(); i = i + 1) {
                                done = done && statusList.get(i).isFinished();
                                success = success && statusList.get(i).isSuccessful();
                            }
                        }
                        //done with statusList
                        statusList.clear();
                        if (success) {
                            MetaServerLog.getInstance().removeChickenFile(filename);
                            this.outputStream.writeObject(NuggetConstants.SUCCESS);
                            this.outputStream.flush();
                        } else {
                            this.outputStream.writeObject(NuggetConstants.FAILURE);
                            this.outputStream.flush();
                        }
                    }
                } else if (function.equals(NuggetConstants.DF)) {
                    String response = "" + NuggetServerInfoLog.getInstance().getFreeSpace();
                    this.outputStream.writeObject(response);
                    this.outputStream.flush();
                } else if (function.equals(NuggetConstants.FORMAT)) {
                    String response = "" + NuggetServerInfoLog.getInstance().getFreeSpace();


                    ArrayList<ThreadStatus> statusList = new ArrayList<ThreadStatus>();
                    for (int i = 0; i < NuggetServerInfoLog.getInstance().getNuggetServers().size(); i = i + 1) {
                        ThreadStatus status = new ThreadStatus(NuggetServerInfoLog.getInstance().getNuggetServers().get(i).getIpAddress());
                        FormatNuggetServer get = new FormatNuggetServer(NuggetServerInfoLog.getInstance().getNuggetServers().get(i), status);
                        statusList.add(status);
                        get.start();
                    }
                    //wait for them to finish
                    //this is not working
                    boolean done = false;
                    boolean success = true;
                    while (!done) {
                        done = true;
                        success = true;
                        for (int i = 0; i < statusList.size(); i = i + 1) {
                            done = done && statusList.get(i).isFinished();
                            success = success && statusList.get(i).isSuccessful();
                        }
                    }
                    //done with statusList
                    statusList.clear();
                    if (success) {
                        MetaServerLog.getInstance().format();
                        NuggetServerInfoLog.getInstance().resetFreeSpace();
                        this.outputStream.writeObject(NuggetConstants.SUCCESS);
                        this.outputStream.flush();
                    } else {
                        this.outputStream.writeObject(NuggetConstants.FAILURE);
                        this.outputStream.flush();
                    }

                    this.outputStream.writeObject(response);
                    this.outputStream.flush();
                }
                else{
                    System.out.println("Bad very bad.");
                }

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    // close streams and connections
                    this.inputStream.close();
                    this.outputStream.close();
                    this.client.close();
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
        }

        private List<NuggetServerInfo> getLocations(int needed) {
            List<NuggetServerInfo> servers = NuggetServerInfoLog.getInstance().getNuggetServers();
            List<NuggetServerInfo> response = new ArrayList<NuggetServerInfo>();
            boolean stop = false;
            for (int j = 0; j < servers.size() && response.size() < needed; j = j + 1) {
                if (servers.get(j).isActive()) {
                    response.add(servers.get(j));
                }
                if (j+1 == servers.size()){
                    j = -1;
                }
            }
            return response;
        }
    }
}

